//////////////////////////////////////////////////////////////////////////////
//
//  Task queue thread
//
//////////////////////////////////////////////////////////////////////////////

#ifndef	__MAGUS_TASK_LOOP_HPP
#define	__MAGUS_TASK_LOOP_HPP

#include <magus/thread/thread.hpp>	
#include <magus/thread/condition.hpp>	
#include <magus/thread/mutex.hpp>	
#include <magus/utility.hpp>		// for noncopyable
#include <queue>
#include <cassert>

//////////////////////////////////////////////////////////////////////////////

namespace magus 
{
	template <typename taskT, typename handlerT>
	class task_loop : private noncopyable, public thread_functor
	{
	public:
		// Constructor & destructor
		task_loop() : _M_running(false), _M_exiting(false), _M_external(false),
			_M_p_handler(new handlerT)
		{ 
			_M_thread = new thread(*this); 
		}
		explicit task_loop(handlerT* p_handler)
			: _M_running(false), _M_exiting(false), 
			_M_external(true), _M_p_handler(p_handler)
		{
			assert(p_handler != NULL);
			_M_thread = new thread(*this);
		}
		
		virtual ~task_loop()
		{
			// set exit flag
			_M_exiting = true;
			// signal to wakeup thread
			_M_not_empty.notify_one();

			// wait until thread exited
			_M_thread->join();
			
			// cleanup
			delete _M_thread;
			if (!_M_external) {
				delete _M_p_handler;
			}
		}

		// is thread running ?
		bool is_running() { return _M_running; }

		int size() { return _M_queue.size(); }

		// add a task to queue
		void add_task(const taskT& t)
		{
			mutex::scoped_lock lock(_M_mtx);
			_M_queue.push(t);
			_M_not_empty.notify_one();
		}

		// Get the handler of this task loop, so client can change its state
		handlerT& get_handler() { assert(_M_p_handler!=NULL); return *_M_p_handler; }

	private:
		bool _M_running;				// thread running flag
		bool _M_exiting;				// thread exiting flag
		bool _M_external;				// handler is external 
		handlerT* _M_p_handler;			// pointer to task handler
		thread* _M_thread;				// pointer to task process thread
		std::queue<taskT> _M_queue;		// task queue
		mutex _M_mtx;					// access task queue mutex
		condition _M_not_empty;			// task queue not empty condition

		void operator()(void)
		{
			_M_running = true;
			taskT task_;
			while (!_M_exiting)
			{
				{
					// wait until queue isn't empty
					mutex::scoped_lock lock(_M_mtx);
					while (!_M_exiting && _M_queue.empty())
						_M_not_empty.wait(lock);
					// should I exit?
					if (_M_exiting)
						break;
					// get a task
					task_ = _M_queue.front();
					_M_queue.pop();
				}
				// call task handler
				(*_M_p_handler)(task_);
			}
			// cleanup tasks in queue
			while (!_M_queue.empty())
			{
				task_ = _M_queue.front();
				_M_queue.pop();
				(*_M_p_handler)(task_);
			}
			_M_running = false;
		}

	}; // class task_loop

} // namespace magus

//////////////////////////////////////////////////////////////////////////////

#endif		// #ifndef	__MAGUS_TASK_LOOP_HPP
